#include "pdfnotes.h"

const QString PdfNotes::ORGNAME = QString("QtGuiProj");
const QString PdfNotes::APPNAME = QString("PdfNotes");

PdfNotes::PdfNotes(QWidget *parent) :
	QMainWindow(parent) {
	ui.setupUi(this);
	doc = 0;
	currentScale = 1.0;
	currentPage = -1;
	pageNoValidator = new QIntValidator(1, 0, this);
	ui.toolBox->setCurrentIndex(0);
	ui.currentPageBox->setValidator(pageNoValidator);

	addDockWidget(Qt::LeftDockWidgetArea, ui.fileViewerWidget);
	ui.toolBar->addWidget(ui.toolBarWidget);

	scaleFactors << tr("25%") << tr("50%") << tr("75%") << tr("100%") << tr("125%") << tr("150%") << tr("200%") << tr("300%");
	scaleFactors << tr("Fit page") << tr("Fit width");
	ui.scaleComboBox->addItems(scaleFactors);
	ui.scaleComboBox->setCurrentIndex(scaleFactors.indexOf("100%"));

	connect(ui.actionSidePanel, SIGNAL(toggled(bool)), ui.fileViewerWidget, SLOT(setVisible(bool)));
	connect(ui.fileViewerWidget, SIGNAL(visibilityChanged(bool)), ui.actionSidePanel, SLOT(setChecked(bool)));

	connect(this, SIGNAL(documentLoaded(Poppler::Document*)), ui.fileViewer, SLOT(documentLoaded(Poppler::Document*)));
	connect(ui.fileViewer, SIGNAL(pageChanged(int)), this, SLOT(setPage(int)));
	connect(this, SIGNAL(pageChanged(int)), ui.fileViewer, SLOT(setPage(int)));
	connect(this, SIGNAL(pageChanged(int)), ui.documentPage, SLOT(changePage(int)));
	connect(this, SIGNAL(scaleChanged(double)), ui.documentPage, SLOT(changeScale(double)));

	connect(ui.actionPrevious_page, SIGNAL(triggered()), this, SLOT(prevPage()));
	connect(ui.prevPage, SIGNAL(clicked()), this, SLOT(prevPage()));
	connect(ui.actionNext_page, SIGNAL(triggered()), this, SLOT(nextPage()));
	connect(ui.nextPage, SIGNAL(clicked()), this, SLOT(nextPage()));

	connect(ui.actionNotes, SIGNAL(toggled(bool)), ui.documentPage, SLOT(addTextNote(bool)));
	connect(ui.actionSelection, SIGNAL(toggled(bool)), ui.documentPage, SLOT(getSelection(bool)));
	connect(ui.actionRectangle, SIGNAL(toggled(bool)), ui.documentPage, SLOT(addRectNote(bool)));
	connect(ui.actionSelect_all, SIGNAL(triggered()), ui.documentPage, 	SLOT(selectAll()));
	connect(ui.documentPage, SIGNAL(textNoteAdded(TextNote *)), this, SLOT(textNoteAdded(TextNote *)));
	connect(ui.documentPage, SIGNAL(rectNoteAdded(RectNote *)), this, SLOT(rectNoteAdded(RectNote *)));
	connect(ui.documentPage, SIGNAL(notesChanged()), this, SLOT(notesChanged()));
	connect(ui.documentPage, SIGNAL(areaSelected(QRectF)), this, SLOT(areaSelected(QRectF)));

	exclusiveActions.insert(ui.actionNotes);
	exclusiveActions.insert(ui.actionRectangle);
	exclusiveActions.insert(ui.actionSelection);

	QCoreApplication::setOrganizationName(ORGNAME);
	QCoreApplication::setApplicationName(APPNAME);
	QCoreApplication::setApplicationVersion(tr("0.4beta"));

	updateRecentFiles(NULL);
}

PdfNotes::~PdfNotes() {
	delete doc;
}

void PdfNotes::on_actionOpen_triggered() {
	QString fileName = QFileDialog::getOpenFileName(this, tr("Open PDF File"), lastPath, tr("PDF files (*.pdf)"));

	if (!fileName.isEmpty())
		openFile(fileName);
}

void PdfNotes::on_actionQuit_triggered() {
	close();
}

void PdfNotes::on_actionClearRecent_triggered() {
	QSettings settings;
	QStringList emptyList;
	settings.setValue("recentFileList", emptyList);
	for (int i = 0; i < MaxRecentFiles; ++i)
		recentFileActs[i]->setVisible(false);
	ui.menuRecentFiles->setEnabled(false);
}

void PdfNotes::on_actionCopy_triggered() {
	QRectF rect = searchLocation;
	QString text;
	bool hadSpace = false;
	QPointF center;
	foreach (Poppler::TextBox *box, doc->page(currentPage)->textList()) {
			if (rect.intersects(box->boundingBox())) {
				if (hadSpace)
					text += " ";
				if (!text.isEmpty() && box->boundingBox().top() > center.y())
					text += "\n";
				text += box->text();
				hadSpace = box->hasSpaceAfter();
				center = box->boundingBox().center();
			}
		}

	if (!text.isEmpty()) {
		QClipboard *clipb = QApplication::clipboard();
		clipb->setText(text);
	} else {
		QMessageBox::information(this, tr("Copy selection"), tr(
				"No text in selected area"));
	}
}

void PdfNotes::on_actionAbout_triggered() {
	QMessageBox::about(
			this,
			tr("About PdfNotes"),
			tr("PdfNotes version 0.9 beta \n\ncreated by MJob & JStezowski \npowered by Qt4 & poppler"));
}

void PdfNotes::on_actionZoom_in_triggered() {
	int idx = ui.scaleComboBox->currentIndex();
	if (++idx < scaleFactors.count() - 3) {
		ui.scaleComboBox->setCurrentIndex(idx);
		on_scaleComboBox_activated(scaleFactors[idx]);
	}
}

void PdfNotes::on_actionZoom_out_triggered() {
	int idx = ui.scaleComboBox->currentIndex();
	if (--idx >= 0) {
		ui.scaleComboBox->setCurrentIndex(idx);
		on_scaleComboBox_activated(scaleFactors[idx]);
	}
}

void PdfNotes::on_actionFitPage_triggered() {
	ui.scaleComboBox->setCurrentIndex(scaleFactors.indexOf("Fit page"));
	on_scaleComboBox_activated("Fit page");
}

void PdfNotes::on_actionFitWidth_triggered() {
	ui.scaleComboBox->setCurrentIndex(scaleFactors.indexOf("Fit width"));
	on_scaleComboBox_activated("Fit width");
}

void PdfNotes::on_actionPrint_triggered() {
	QPrinter printer;
	QPrintDialog dlg(&printer, this);
	dlg.setMinMax(1, doc->numPages());
	if (dlg.exec() == QDialog::Accepted) {
		PrintThread thr(printer, doc, notes);
		thr.setRange(dlg.fromPage(), dlg.toPage());
		thr.run();
	}
}

void PdfNotes::on_actionSelection_toggled(bool t) {
	if (t) {
		uncheckOthers(ui.actionSelection);
	} else {
		searchLocation = QRectF();
		showPage();
	}
}

void PdfNotes::on_actionSave_as_triggered() {
	QFileDialog dialog(this);
	dialog.setFilter(tr("Portable Document Format (*.pdf)"));
	if(dialog.exec()){
		QStringList list = dialog.selectedFiles();
		for(int i=0;i<list.size();i++){
			//std::cout<<list.at(i).toStdString()<<"\n";
			QString dest(list.at(i));
			if(!dest.endsWith(".pdf",Qt::CaseInsensitive)){
				dest+=tr(".pdf");
			}
			if(doc!=NULL && lastPath!=NULL){
				std::fstream source,destination;
				source.open(lastPath.toStdString().c_str(),std::ios::in);
				destination.open(dest.toStdString().c_str(),std::ios::out|std::ios::trunc);
				char c;
				while(source.good()){
					c= source.get();
					if(source.good()){
						destination<<c;
					}
				}
				source.close();
				destination.close();
				saveNotesToFile(dest);
			}
		}
	}

}

void PdfNotes::saveNotesToFile(const QString & path)
{
    QFile data(path);
    data.open(QFile::WriteOnly | QFile::Append);
    QTextStream stream(&data);
    QString s("\n%%PDF Notes Declaration Start\n");
    stream << s;

    for (int p=0; p<doc->numPages(); p++){
        PageNotes *n = notes.getNotesForPage(p);

        int size = n->rectNotes().size();
        for(int i = 0;i < size;i++){
            RectNote note = n->rectNotes().at(i);
            if(note.valid())
                stream << "%%PdfRectNote " << p << " " << note.rectangle().x() << " " << note.rectangle().y() << " " << note.rectangle().width() << " " << note.rectangle().height() << " " << note.text().replace(QString("\n"), QString("<br/>"), Qt::CaseInsensitive) << "\n";

        }
        size = n->textNotes().size();
        for(int i = 0;i < size;i++){
            TextNote note = n->textNotes().at(i);
            if(note.valid())
                stream << "%%PdfTextNote " << p << " " << note.location().x() << " " << note.location().y() << " " << note.text().replace(QString("\n"), QString("<br/>"), Qt::CaseInsensitive) << "\n";

        }
    }

    data.close();
}

void PdfNotes::on_actionSave_triggered() {
    saveNotesToFile(lastPath);
}

void PdfNotes::on_actionConfigure_triggered() {
	ConfigDialog dlg(this);
	dlg.exec();
	if (doc)
		showPage();
}

void PdfNotes::nextPage() {
	int newPage = currentPage + 1;
	if (doc != 0 && newPage < doc->numPages())
		setPage(newPage);
}

void PdfNotes::prevPage() {
	int newPage = currentPage - 1;
	if (doc != 0 && newPage > -1)
		setPage(newPage);
}

void PdfNotes::on_currentPageBox_returnPressed() {
	bool ok = 0;
	int newPage = ui.currentPageBox->text().toInt(&ok);

	if (ok && doc != 0 && newPage > 0 && newPage <= doc->numPages()) {
		setPage(newPage - 1);
	}
}

void PdfNotes::on_scaleComboBox_activated(const QString &text) {
	if (doc == 0)
		return;

	QString txt = text;
	bool ok;
	double scale = txt.remove(QChar('%')).toDouble(&ok);

	if (ok) {
		currentScale = scale / 100;
	} else if (text == tr("Fit page")) {
		currentScale = calcScaleFitPage();
	} else if (text == tr("Fit width")) {
		currentScale = calcScaleFitWidth();
	}
	emit scaleChanged(currentScale);
	showPage();
}

void PdfNotes::searchDocument(bool backwards) {
	if (ui.searchLine->text().isNull() || ui.searchLine->text().isEmpty())
		return;

	QRectF location;

	if (backwards) {
		location = searchBackwards(ui.searchLine->text());
	} else {
		location = searchForwards(ui.searchLine->text());
	}

	QPoint target =
			ui.documentPage->matrix().mapRect(location).center().toPoint();
	ui.pageScrollArea->ensureVisible(target.x(), target.y());
}

void PdfNotes::openRecentFile() {
	QAction *action = qobject_cast<QAction *> (sender());
	if (action)
		openFile(action->data().toString());

}

void PdfNotes::openFile(const QString &path) {
	Poppler::Document *oldDocument = doc;

	doc = Poppler::Document::load(path);
	if (doc) {
		delete oldDocument;
		doc->setRenderHint(Poppler::Document::Antialiasing);
		doc->setRenderHint(Poppler::Document::TextAntialiasing);
		searchLocation = QRectF();

		lastPath = path;
		currentPage = -1;
		emit
		documentLoaded(doc);

		ui.pageCountLabel->setText(tr("%1").arg(doc->numPages()));
		pageNoValidator->setTop(doc->numPages());

		enableActions();
		updateRecentFiles(path);
		notes.clear();
		notes.loadFromFile(lastPath);

		setPage(0);
	} else {
		QMessageBox::warning(this, tr("Failed to Open File"), tr(
				"The specified file could not be opened."));
	}
}

void PdfNotes::setPage(int pageNo) {
	if (pageNo != currentPage) {
		searchLocation = QRectF();
		showPage(pageNo);
		if (searchLocation.isEmpty())
			ui.pageScrollArea->ensureVisible(0, 0);
	}
}

void PdfNotes::showPage(int pageNo) {
	if (pageNo != -1 && pageNo != currentPage) {
		currentPage = pageNo;
		emit pageChanged(pageNo);
	}
	QImage image = doc->page(currentPage)->renderToImage(currentScale
			* physicalDpiX(), currentScale * physicalDpiY());
	ui.documentPage->setFixedSize(image.size());
	ui.documentPage->setFrameShape(QFrame::WinPanel);
	ui.currentPageBox->setText(tr("%1").arg(currentPage + 1));

	ui.documentPage->setSearchLocation(searchLocation);
	ui.documentPage->setNotes(notes.getNotesForPage(currentPage));
	ui.documentPage->setImg(image);
}

double PdfNotes::calcScaleFitPage() {
	QSizeF availableSize = ui.pageScrollArea->size();
	QSizeF pageSize = doc->page(currentPage)->pageSizeF();

	qreal wscale = availableSize.width() * 0.72 / pageSize.width();
	qreal hscale = availableSize.height() * 0.72 / pageSize.height();

	return qMin(wscale, hscale);
}

double PdfNotes::calcScaleFitWidth() {
	QSizeF availableSize = ui.pageScrollArea->size();
	QSizeF pageSize = doc->page(currentPage)->pageSizeF();

	qreal wscale = availableSize.width() * 0.72 / pageSize.width() - 0.02;

	return wscale;
}

QRectF PdfNotes::searchForwards(const QString &text) {
	int page = currentPage;
	while (page < doc->numPages()) {

		if (doc->page(page)->search(text, searchLocation,
				Poppler::Page::NextResult, Poppler::Page::CaseInsensitive)) {
			if (!searchLocation.isNull()) {
				showPage(page);
				return searchLocation;
			}
		}
		page += 1;
		searchLocation = QRectF();
	}

	page = 0;
	while (page < currentPage) {
		searchLocation = QRectF();
		if (doc->page(page)->search(text, searchLocation,
				Poppler::Page::NextResult, Poppler::Page::CaseInsensitive)) {
			if (!searchLocation.isNull()) {
				showPage(page);
				return searchLocation;
			}
		}
		page += 1;
	}

	return QRectF();
}

QRectF PdfNotes::searchBackwards(const QString &text) {
	QRectF oldLocation = searchLocation;

	int page = currentPage;
	if (oldLocation.isNull())
		page -= 1;

	while (page > -1) {
		QList<QRectF> locations;
		searchLocation = QRectF();

		while (doc->page(page)->search(text, searchLocation,
				Poppler::Page::NextResult, Poppler::Page::CaseInsensitive)) {
			if (searchLocation != oldLocation)
				locations.append(searchLocation);
			else
				break;
		}

		int index = locations.indexOf(oldLocation);
		if (index == -1 && !locations.isEmpty()) {
			searchLocation = locations.last();
			showPage(page);
			return searchLocation;
		} else if (index > 0) {
			searchLocation = locations[index - 1];
			showPage(page);
			return searchLocation;
		}

		oldLocation = QRectF();
		page -= 1;
	}

	if (currentPage == doc->numPages() - 1)
		return QRectF();

	oldLocation = QRectF();
	page = doc->numPages() - 1;

	while (page > currentPage) {

		QList<QRectF> locations;
		searchLocation = QRectF();

		while (doc->page(page)->search(text, searchLocation,
				Poppler::Page::NextResult, Poppler::Page::CaseInsensitive)) {

			locations.append(searchLocation);
		}

		if (!locations.isEmpty()) {
			searchLocation = locations.last();
			showPage(page);
			return searchLocation;
		}
		page -= 1;
	}

	return QRectF();

}

void PdfNotes::textNoteAdded(TextNote *note) {
	notes.addNote(note);
	searchLocation = QRectF();
	uncheckOthers(0);
	showPage();
}

void PdfNotes::rectNoteAdded(RectNote *note) {
	notes.addNote(note);
	searchLocation = QRectF();
	uncheckOthers(0);
	showPage();
}

void PdfNotes::notesChanged() {
	searchLocation = QRectF();
	uncheckOthers(0);
	showPage();
}

void PdfNotes::areaSelected(QRectF rect) {
	searchLocation = rect;
	showPage();
}

void PdfNotes::updateRecentFiles(const QString &fileName) {
	QSettings settings;
	if (fileName.isNull()) {
		for (int i = 0; i < MaxRecentFiles; ++i) {
			recentFileActs[i] = new QAction(this);
			recentFileActs[i]->setVisible(false);
			connect(recentFileActs[i], SIGNAL(triggered()), this,
					SLOT(openRecentFile()));
			ui.menuRecentFiles->addAction(recentFileActs[i]);
		}
		ui.menuRecentFiles->addSeparator();
		ui.menuRecentFiles->addAction(ui.actionClearRecent);
	} else {
		QStringList files = settings.value("recentFileList").toStringList();
		files.removeAll(fileName);
		files.prepend(fileName);
		while (files.size() > MaxRecentFiles)
			files.removeLast();
		settings.setValue("recentFileList", files);
	}

	QStringList files = settings.value("recentFileList").toStringList();
	int numRecentFiles = qMin(files.size(), (int) MaxRecentFiles);

	for (int i = 0; i < numRecentFiles; ++i) {
		QString text = tr("&%1 %2").arg(i + 1).arg(
				QFileInfo(files[i]).fileName());
		recentFileActs[i]->setText(text);
		recentFileActs[i]->setData(files[i]);
		recentFileActs[i]->setVisible(true);
	}
	for (int j = numRecentFiles; j < MaxRecentFiles; ++j)
		recentFileActs[j]->setVisible(false);

	ui.menuRecentFiles->setEnabled(numRecentFiles > 0);
}

void PdfNotes::enableActions() {
	//menus and actions
	ui.actionReopen->setEnabled(true);
	ui.actionPrint->setEnabled(true);
	ui.actionSave->setEnabled(true);
	ui.actionSave_as->setEnabled(true);
	ui.menuNotes->setEnabled(true);
	ui.actionNotes->setEnabled(true);
	ui.actionRectangle->setEnabled(true);
	ui.actionSelection->setEnabled(true);
	ui.actionSelect_all->setEnabled(true);
	ui.actionFind->setEnabled(true);
	ui.actionCopy->setEnabled(true);
	ui.actionPrevious_page->setEnabled(true);
	ui.actionNext_page->setEnabled(true);
	ui.actionFirst_page->setEnabled(true);
	ui.actionLast_page->setEnabled(true);
	ui.actionZoom_in->setEnabled(true);
	ui.actionZoom_out->setEnabled(true);
	ui.actionFitWidth->setEnabled(true);
	ui.actionFitPage->setEnabled(true);

	//toolbar elements
	ui.prevPage->setEnabled(true);
	ui.nextPage->setEnabled(true);
	ui.currentPageBox->setEnabled(true);
	ui.scaleComboBox->setEnabled(true);
	ui.searchLine->setEnabled(true);
	ui.searchFwd->setEnabled(true);
	ui.searchBwd->setEnabled(true);
	ui.fileViewer->setEnabled(true);
}

void PdfNotes::uncheckOthers(QAction *action) {
	QAction *a;
	foreach (a, exclusiveActions) {
			if (a != action)
				a->setChecked(false);
		}
}
